package com.mystudio.android.calendar.view;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ListView;

import com.mystudio.android.calendar.MonthActivity;
import com.mystudio.android.calendar.adaptor.CalendarAdapter;
import com.mystudio.android.calendar.constants.Constants;
import com.mystudio.android.calendar.model.CalendarConfig;
import com.mystudio.android.calendar.model.Instance;
import com.mystudio.android.util.Logger;

public class MonthListView extends ListView implements OnTouchListener, OnItemClickListener, OnItemLongClickListener, OnItemSelectedListener {
	private static final Logger logger = new Logger(Constants.LOG_TAG, MonthListView.class);
	private CalendarConfig config;
	//private int centerX, centerY;
	private OnMonthChangeListener monthChangeListener;
	private OnDaySelectedListener daySelectedListener;
	//private GestureDetector gestureDetector = null;
	private Time tempTime = new Time();
	private int year = 0;
	private int month = 0;
	private int yearWhenDown = 0;
	private int monthWhenDown = 0;
	private VelocityTracker mVelocityTracker;
	public int downJulianDay = 0;
	
	public MonthListView(Context context, AttributeSet attributes) {
		super(context, attributes);
		
        //setFocusable(true);
        //setClickable(true);

		CalendarConfig lConfig = config = new CalendarConfig();
		lConfig.initialize(context);
		
		this.setAdapter(new CalendarAdapter(context, lConfig));
		
		this.setCacheColorHint(lConfig.mMonthBorderColor);
		this.setVerticalFadingEdgeEnabled(false);
		//setOnTouchListener(this);
		//super.setOnItemClickListener(this);
		//super.setOnItemLongClickListener(this);
		//super.setOnItemSelectedListener(this);
		
		// Check if changing current month needed on scroll ends 
		setOnScrollListener(new OnScrollListener() {
			private int lastCenterItem = 0;
			
		    public void onScrollStateChanged(AbsListView view, int scrollState) {
		    }

			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {

				if (visibleItemCount != 0 && monthChangeListener != null) {
					int centerItem = firstVisibleItem + visibleItemCount/2 - 1;
					if (centerItem != lastCenterItem) {
						CalendarAdapter adapter = ((CalendarAdapter)getAdapter());
						Time lTime = tempTime;
						lTime.setJulianDay(adapter.position2JulianDay(centerItem, config)+3);
						if (lTime.month != adapter.currentMonth) {
							adapter.setCurrentMonth(lTime.month);
							for (int i=0; i<getChildCount(); ++i) {
								((WeekInMonthView)getChildAt(i)).setCurrentMonth(lTime.month);
							}
							year = lTime.year;
							month = lTime.month;
							monthChangeListener.onCurrentMonthChanged(lTime.year, lTime.month);
						}

						lastCenterItem = centerItem;
					}
				}
			}
		});
		
		//gestureDetector = new GestureDetector(getContext(), this);
	}
	
    @Override
    protected void onSizeChanged(int width, int height, int oldw, int oldh) {
    	CalendarConfig lConfig = config;
    	lConfig.changeCellDimension(width, height);
		((CalendarAdapter)getAdapter()).setConfig(lConfig);
		for (int i=0; i<getChildCount(); ++i) {
			((WeekInMonthView)getChildAt(i)).setConfig(lConfig);
		}
		//centerX = width/2;
		//centerY = height/2;
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();

        CalendarConfig lConfig = config;
        
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        VelocityTracker velocityTracker = mVelocityTracker;
        velocityTracker.addMovement(ev);
    	
        boolean skipDefaultHandle = false;
        switch (action & MotionEvent.ACTION_MASK) {
        	case MotionEvent.ACTION_DOWN:
        		monthWhenDown = month;
        		yearWhenDown = year;
        		break;
        		
        	case MotionEvent.ACTION_MOVE:
       			downJulianDay = 0;
        		break;

        	case MotionEvent.ACTION_UP:
    			if (downJulianDay == 0) {
	        		velocityTracker.computeCurrentVelocity(1000);
	        		//logger.info("Action up: " + velocityTracker.getYVelocity());
	                if (Math.abs(velocityTracker.getYVelocity()) > 1000) {
	                	if (getChildCount() > 0) {
	            			Time lTime = tempTime;
	            			lTime.set(1, monthWhenDown, yearWhenDown);
	                		if (velocityTracker.getYVelocity() < 0) {
	                			lTime.month++;
	                		} else {
	                			lTime.month--;
	                		}
	            			lTime.normalize(true);
	            			setMonth(lTime.year, lTime.month, false);
	                    	skipDefaultHandle = true;
	                	}
	                }
    			} else {
        			for (int i=0; i<getChildCount(); ++i) {
        				WeekInMonthView child = ((WeekInMonthView)getChildAt(i));
        				
        				// clear any previous selected day
        				if (child.selectedJulianDay != 0) {
        					child.selectedJulianDay = 0;
        					child.invalidate();
        				}
        				
        				// set new selected day
        				if (downJulianDay >= child.mFirstJulianDay && downJulianDay < child.mFirstJulianDay+7) {
        					if (child.mInstances != null && daySelectedListener != null) {
	        					child.selectedJulianDay = downJulianDay;
	        					child.invalidate();
	        					((CalendarAdapter)getAdapter()).selectedJulianDay = downJulianDay; // to ensure any newly generated week view would highlight selected day if needed
	        					daySelectedListener.onDaySelected(downJulianDay, child.mInstances);
        					}
        				}
        			}
    			}
                break;
        }
        
        boolean handled = true;
        if (!skipDefaultHandle) {
        	handled = super.onTouchEvent(ev);
        }
        //logger.info("onTouch: " + handled);

		//gestureDetector.onTouchEvent(ev);
		/*
    	if (!handled) {
    		handled = super.onTouchEvent(ev);
    	}
    	*/
    	return handled;
    }

    /*
    @Override
	public boolean onDown(MotionEvent e) {
logger.info("MonthListView.onDown");
		yearWhenDown = year;
		monthWhenDown = month;
		return true;
	}

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
logger.info("MonthListView.onFling: " + velocityY);
		if (Math.abs(velocityY) > 500 && getChildCount() > 0 && yearWhenDown != 0 && monthWhenDown != 0) {
			
			//int firstWeekJulianDay = ((WeekInMonthView)getChildAt(0)).mFirstJulianDay;
			//if (firstWeekJulianDay != 0) {
			//	tempTime.setJulianDay(firstWeekJulianDay);
			//	if (velocityY < 0) {
			//		tempTime.month++;
			//	} else {
			//		tempTime.month--;
			//	}
			//	tempTime.normalize(true);
//logger.info("MonthListView.onFling toMonth: " + tempTime.month);
			//	setMonth(tempTime.year, tempTime.month, false);
			//}
			
			tempTime.set(1, monthWhenDown, yearWhenDown);
			if (velocityY < 0) {
				tempTime.month++;
			} else {
				tempTime.month--;
			}
			tempTime.normalize(true);
			logger.info("MonthListView.onFling toMonth: " + tempTime.month);
			setMonth(tempTime.year, tempTime.month, true);

			return true;
		}
		
		return false;
	}

	@Override
	public void onLongPress(MotionEvent e) {
logger.info("MonthListView.onLongPress");
	}

	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
			float distanceY) {
logger.info("MonthListView.onScroll");
		return false;
	}

	@Override
	public void onShowPress(MotionEvent e) {
logger.info("MonthListView.onShowPress");
	}

	@Override
	public boolean onSingleTapUp(MotionEvent e) {
logger.info("MonthListView.onSingleTapUp");
		return false;
	}
	*/
	
    public void setParentActivity(Activity parentActivity) {
    	((CalendarAdapter)getAdapter()).setParentActivity(parentActivity);
    }
    
    public void setOnMonthChangeListener(OnMonthChangeListener listener) {
    	monthChangeListener = listener;
    }
    
    public void setOnDaySelectedListener(OnDaySelectedListener listener) {
    	daySelectedListener = listener;
    }
    
    public void setMonth(int year, int month) {
    	setMonth(year, month, false);
    }
    
    /*
    private void reCalcCurrentMonth() {
    	CalendarAdapter adapter = ((CalendarAdapter)getAdapter());
		tempTime.setJulianDay(adapter.position2JulianDay(pointToPosition(centerX, centerY), config));
		if (tempTime.month != adapter.currentMonth) {
			adapter.setCurrentMonth(tempTime.month);
			for (int i=0; i<getChildCount(); ++i) {
				((WeekInMonthView)getChildAt(i)).setCurrentMonth(tempTime.month);
			}
			monthChangeListener.onCurrentMonthChanged(tempTime.year, tempTime.month);
		}
    }
    */

	/**
	 * Jump to specified year+month (0-11)
	 */
	private void setMonth(int year, int month, boolean smoothScroll) {
		// Set current month no. and the first julian day of the first week of this month
		((CalendarAdapter)getAdapter()).setCurrentMonth(month);
		for (int i=0; i<getChildCount(); ++i) {
			((WeekInMonthView)getChildAt(i)).setCurrentMonth(month);
		}
		Time lTime = tempTime;
		lTime.set(0,0,13,1,month,year);
		lTime.normalize(true);
		long millis = lTime.toMillis(true);
        int dayOneJulianDay = Time.getJulianDay(millis, lTime.gmtoff);
        int offset = lTime.weekDay - config.weekStartDay;
        if (offset < 0) {
        	offset += 7;
        }
        if (smoothScroll) {
        	this.smoothScrollToPosition(julianDay2Position(dayOneJulianDay - offset));
        } else {
        	this.setSelection(julianDay2Position(dayOneJulianDay - offset));
        }

        this.year = year;
        this.month = month;
        monthChangeListener.onCurrentMonthChanged(year, month);
	}
	
	public void setMonthAsCurrent() {
		Time lTime = tempTime;
		lTime.setToNow();
        setMonth(lTime.year, lTime.month);
	}
	
	private int julianDay2Position(int julianDay) {
		// Zero Julian day is Monday
		return (int)(Math.floor(julianDay - config.weekStartDay + 1) / 7.0);
	}

	@Override
	public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
		logger.info("onClick");
	}

	@Override
	public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2,
			long arg3) {
		logger.info("onItemSelected");
	}

	@Override
	public void onNothingSelected(AdapterView<?> arg0) {
		logger.info("onNothingSelected");
	}

	@Override
	public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,
			long arg3) {
		logger.info("onItemLongClick");
		return false;
	}

	@Override
	public boolean onTouch(View arg0, MotionEvent arg1) {
		logger.info("onTouch");
		return false;
	}
/*	
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		if (ev.getAction() == MotionEvent.ACTION_DOWN) {
			yearWhenDown = year;
			monthWhenDown = month;
		}
		
		return super.dispatchTouchEvent(ev);
	}
*/
}
